package com.sprite.framework.start;

import com.sprite.framework.context.ContextEnvironment;
import com.sprite.framework.start.util.ContainerUtils;
import com.sprite.utils.UtilBeans;
import com.sprite.utils.UtilMisc;
import com.sprite.utils.UtilString;
import com.sprite.utils.UtilXml;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.CharBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

class Loader {

	private static Logger logger = LoggerFactory.getLogger(Loader.class);

	private List<Container> containers = new LinkedList<>();

	private int localPort = 8005;	// 本地端口号
	private String cmd_shutdown = "shutdown";	// 停止服务的指令

	private boolean safe_shutdown = false;	

	private volatile boolean started;

	public Loader() {}

	public boolean isSafeShutdown() {
		return safe_shutdown;
	}

	public void init(Document document, String[] args) {
		if(document == null) {
			throw new IllegalArgumentException("[Loader][init] document is null");
		}
		Element root = document.getDocumentElement();

		localPort = UtilMisc.castToInt(root.getAttribute("port"), 8005);
		cmd_shutdown = root.getAttribute("shutdown");

		safe_shutdown =  "true".equals(root.getAttribute("safeShutdown"));

		setEvement(root);
		
		initContainers(root, args);
	}


	protected static void setEvement(Element root) {
		
		List<? extends Element> elements = UtilXml.childElementList(root, "properties");

		for(Element element : elements) {
			
			// 是否添加到上下文
			boolean putToSystem = "system".equals(element.getAttribute("model"));
			
			List<? extends Element> properties = UtilXml.childElementList(element, "property");
			for(Element pElement : properties) {
				String key  = ContainerUtils.getPropertyKey(pElement);
				String value = ContainerUtils.getPropertyValue(pElement);
				if(!putToSystem){
					ContextEnvironment.put(key, value);
				}else{
					System.setProperty(key, value);
				}
			}

			// 加载properties文件
			properties = UtilXml.childElementList(element, "properties");
			for(Element pElement : properties) {
				String path  = pElement.getAttribute("location");
				try {
					ContextEnvironment.loadProperties(path, putToSystem);
				} catch (IOException e) {
					logger.error("load properties error path:"+path, e );
				}
			}
		}
	}

	protected void initContainers(Element root, String[] args) {
		List<? extends Element> elements = UtilXml.childElementList(root, "containers");

		List<String> list = new LinkedList<>();
		for(Element element : elements) {
			String containers = element.getTextContent();
			List<String> classList = UtilString.commaDelimiteToStringList(containers);
			for(String clazzName : classList) {
				list.add(clazzName.trim());
			}
		}

		for(String containerClass : list) {
			try {
				Class<?> clazz = Class.forName(containerClass);
				if(Container.class.isAssignableFrom(clazz)) {
					Container container = (Container)UtilBeans.instance(clazz);
					container.init(args);
					containers.add(container);
				}
			} catch (ClassNotFoundException e) {
				throw new StartExecption("not found container", e);
			} catch (ReflectiveOperationException e ) {
				throw new StartExecption("instance error container", e);
			}
		}
	}
	
	
	public void start() {
		if(started) {
			return;
		}

		// batch start Containers
		try {
			if(containers == null || containers.isEmpty()) {
				logger.info("containers is empty ");
				return;
			}
			for(Container container: containers) {
				container.start();
			}

			started = true;
		} catch (Exception e) {
			throw new StartExecption("start error", e);
		}
	}

	public void stop() {
		try {
			if(containers == null || containers.isEmpty()) {
				logger.info("containers is empty ");
				return;
			}

			List<Container> containers = new LinkedList<>(this.containers);
			Collections.reverse(containers);

			for(Container container: containers) {
				try {
					logger.info("stoping container: "+ container.getClass().getName());
					container.stop();
					logger.info("stoped container: "+ container.getClass().getName());
				} catch (Exception e) {
					logger.info("stoped container: "+ container.getClass().getName());
				}
			}
		} catch (Exception e) {
			logger.error("stop error ", e );
		}finally {
			started = false;
		}
	}

	/**
	 * 停止一个 已经存在的Loader
	 */
	public void stopLoader() {
		if(!safe_shutdown) {
			return;
		}

		try(Socket socket = new Socket("localhost", localPort)){
			OutputStream stream = socket.getOutputStream();
			stream.write(cmd_shutdown.getBytes());
			stream.flush();
		}catch (Exception e) {
			logger.error("[Loader] stopLoader error ", e );
			System.exit(1);
		}
	}

	private ServerSocket serverSocket;

	/**
	 * 阻塞当前线程，等待停止指令
	 */
	public void awaitToStop() {
		if(!safe_shutdown) {
			logger.info("[Loader][awaitToStop] skip awaitToStop, safe_shutdown: ", safe_shutdown);
			return;
		}
		Socket socket = null;

		try {
			serverSocket = new ServerSocket(localPort);

			while(started) {
				socket = serverSocket.accept();
				System.out.println("new accept!");
				Thread.sleep(5*1000);
				try {
					BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
					StringBuilder cmd = new StringBuilder();
					CharBuffer charBuffer = CharBuffer.allocate(1024);

					while(reader.read(charBuffer) != -1) {
						charBuffer.flip();
						cmd.append(charBuffer);
					}

					String commandLine = cmd.toString();
					if(cmd_shutdown.equals(commandLine)) {
						break;
					}
					socket.close();
				}catch (Exception e) {
					logger.info("[Loader][awaitToStop] skip to await one socket ", e );
				}
			}

			socket.close();
			serverSocket.close();
		} catch (IOException | InterruptedException e) {
			logger.error("[Loader] awaitToStop error ", e );
			System.exit(1);
		}finally {
			serverSocket = null;
		}
		this.stop();
	}
}
